/**********************************************************************************
 * Module Name: cordic_process
 * Description: 
 * This module controls a CORDIC IP core to process a block of data.
 * - It waits for a 'start_flag' pulse.
 * - Upon starting, it asserts 'busy'.
 * - It reads 1536 pairs of (cos, sin) data from two input BRAMs.
 * - It feeds the data into a CORDIC IP core.
 * - It writes the 1536 results from the CORDIC core into a result BRAM.
 * - It de-asserts 'busy' upon completion and waits for the next start signal.
 * Assumptions:
 * - The BRAMs have a read latency of 1 clock cycle.
 * - The CORDIC IP core's result is 32 bits, and the desired 16-bit output
 * is in the upper 16 bits of the result (cordic_result[31:16]).
 **********************************************************************************/
(*DONT_TOUCH = "TRUE"*)
module codic_process #(
    parameter mux_num = 1536
)(
    input                               clk,
    input                               rst_n,

    // Interface to SIN BRAM (Read)
    input      [15:0]                   daout_bram_r_sin_data,
    output reg [$clog2(mux_num)-1:0]    daout_bram_r_sin_addr,
    output reg                          daout_bram_r_sin_en,

    // Interface to COS BRAM (Read)
    input      [15:0]                   daout_bram_r_cos_data,
    output reg [$clog2(mux_num)-1:0]    daout_bram_r_cos_addr,
    output reg                          daout_bram_r_cos_en,

    // Interface to Result BRAM (Write)
    output reg [15:0]                   result_bram_w_data,
    output reg [$clog2(mux_num)-1:0]    result_bram_w_addr,
    output reg                          result_bram_w_en,
    output reg                          result_bram_w_we,

    // Control Signals
    input                               start_flag,
    output                              busy
);

// FSM state definitions
localparam S_IDLE = 1'b0;
localparam S_RUN  = 1'b1;

// Internal signals
reg  state, next_state;
reg  busy_reg;

reg  [$clog2(mux_num)-1:0] read_addr_cnt;
reg  [$clog2(mux_num)-1:0] write_addr_cnt;

// CORDIC IP interface signals
wire [31:0] cordic_input_data;
wire [31:0] cordic_result;
wire        cordic_out_valid;
reg         cordic_in_valid;

//----------------------------------------------------------------
// CORDIC IP Core Instantiation
//----------------------------------------------------------------
cordic_0 u_cordic_0 (
    .aclk(clk),
    // Assuming 1-cycle BRAM read latency, valid signal is delayed by 1 cycle
    .s_axis_cartesian_tvalid(cordic_in_valid),
    .s_axis_cartesian_tdata(cordic_input_data),
    .m_axis_dout_tvalid(cordic_out_valid),
    .m_axis_dout_tdata(cordic_result)
);

// Prepare data for CORDIC input. Assuming cos is MSB part.
assign cordic_input_data = {daout_bram_r_cos_data, daout_bram_r_sin_data};
assign busy = busy_reg;

//----------------------------------------------------------------
// FSM Sequential Logic (State Register)
//----------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= S_IDLE;
    end else begin
        state <= next_state;
    end
end

//----------------------------------------------------------------
// FSM Combinational Logic (Next State and Output Logic)
//----------------------------------------------------------------
always @(*) begin
    // Default values
    next_state = state;
    daout_bram_r_sin_en = 1'b0;
    daout_bram_r_cos_en = 1'b0;
    result_bram_w_en = 1'b0;
    result_bram_w_we = 1'b0;

    case (state)
        S_IDLE: begin
            if (start_flag) begin
                next_state = S_RUN;
            end
        end

        S_RUN: begin
            // Enable reading as long as we have data to read
            if (read_addr_cnt < mux_num) begin
                daout_bram_r_sin_en = 1'b1;
                daout_bram_r_cos_en = 1'b1;
            end

            // Enable writing when CORDIC output is valid
            if (cordic_out_valid) begin
                result_bram_w_en = 1'b1;
                result_bram_w_we = 1'b1; // Assuming full word write
            end
            
            // If the last piece of data has been written, go back to IDLE
            if (write_addr_cnt == mux_num - 1 && cordic_out_valid) begin
                next_state = S_IDLE;
            end
        end

        default: begin
            next_state = S_IDLE;
        end
    endcase
end

//----------------------------------------------------------------
// Control, Counters, and BRAM Interface Logic
//----------------------------------------------------------------
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        busy_reg <= 1'b0;
        read_addr_cnt <= 0;
        write_addr_cnt <= 0;
        cordic_in_valid <= 1'b0;
        daout_bram_r_sin_addr <= 0;
        daout_bram_r_cos_addr <= 0;
        result_bram_w_addr <= 0;
        result_bram_w_data <= 0;
    end else begin
        // State transitions logic
        if (state == S_IDLE && next_state == S_RUN) begin
            busy_reg <= 1'b1;
            read_addr_cnt <= 0;
            write_addr_cnt <= 0;
        end else if (state == S_RUN && next_state == S_IDLE) begin
            busy_reg <= 1'b0;
        end

        // Address counters and data handling logic
        if (state == S_RUN) begin
            // Read address generation
            if (daout_bram_r_sin_en) begin // or _cos_en, they are the same
                daout_bram_r_sin_addr <= read_addr_cnt;
                daout_bram_r_cos_addr <= read_addr_cnt;
                if(read_addr_cnt < mux_num) begin
                    read_addr_cnt <= read_addr_cnt + 1;
                end
            end
            
            // CORDIC input valid signal generation (1 cycle delay after read enable)
            cordic_in_valid <= daout_bram_r_sin_en;

            // Write address and data generation
            if (result_bram_w_en) begin // which means cordic_out_valid is high
                result_bram_w_addr <= write_addr_cnt;
                // IMPORTANT: Extracting upper 16 bits from 32-bit CORDIC result.
                // Modify this if you need a different part of the result.
                result_bram_w_data <= cordic_result[15:0]; 
                write_addr_cnt <= write_addr_cnt + 1;
            end
        end
    end
end

endmodule